package com.company.project.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.company.project.config.ThreadPoolComponent;
import com.company.project.mapper.TopologyHistoryMapper;
import com.company.project.model.Topology;
import com.company.project.model.TopologyHistory;
import com.company.project.service.NetworkService;
import com.company.project.util.DockerUtil;
import com.company.project.util.NetworkUtil;
import com.company.project.vo.req.AttackRecordVo;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;


@Slf4j
@Service
public class NetworkServiceImpl implements NetworkService {

    // Key: containerName, Value: containerId
    private final Map<String, String> containerMap = new HashMap<>();
    private Topology currentTopology; // 保存当前加载的拓扑

    @Autowired
    private ThreadPoolComponent threadPoolComponent;

    @Resource
    private TopologyHistoryMapper topologyHistoryMapper;

    @Override
    public void loadTopology(String topologyFilePath) throws Exception {
        // 提取文件名
        String fileName = new File(topologyFilePath).getName();
        // 计算文件哈希值
        String fileHash = calculateFileHash(new File(topologyFilePath));

        // 调用重载方法
        loadTopology(topologyFilePath, fileName, fileHash);
    }
    @Override
    public void loadTopology(String topologyFilePath, String fileName, String fileHash) throws Exception {
        //每次加载前先清除当前网络
        clearNetwork();
        ObjectMapper mapper = new ObjectMapper();
        try {
            log.info("Loading topology from file: {}", topologyFilePath);
            File topologyFile = new File(topologyFilePath);
            if (!topologyFile.exists()) {
                throw new IOException("Topology file not found: " + topologyFilePath);
            }

            currentTopology = mapper.readValue(topologyFile, Topology.class);
            log.info("Topology loaded successfully: {} nodes, {} links",
                    currentTopology.getNodes().size(),
                    currentTopology.getLinks().size());
            // 使用 Mapper 保存历史记录
            TopologyHistory history = new TopologyHistory(fileName, topologyFilePath, fileHash);
            // 检查是否存在相同哈希值的记录
            LambdaQueryWrapper<TopologyHistory> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(TopologyHistory::getFileHash, fileHash);
            TopologyHistory existing = topologyHistoryMapper.selectOne(wrapper);

            if (existing != null) {
                // 更新现有记录
                log.info("Updating existing topology history record: {}", existing.getId());
                existing.setCreateTime(new Date());
                int updated = topologyHistoryMapper.updateById(existing);
                log.info("Updated topology history record: {}, result: {}", existing.getId(), updated);
            } else {
                // 插入新记录
                log.info("Inserting new topology history record");
                int inserted = topologyHistoryMapper.insert(history);
                log.info("Inserted topology history record: {}, result: {}", history.getId(), inserted);
            }
        } catch (JsonParseException e) {
            log.error("Failed to parse topology file: Invalid JSON format", e);
            throw new RuntimeException("Failed to parse topology file: Invalid JSON format", e);
        } catch (IOException e) {
            log.error("Failed to read topology file: File not found or inaccessible", e);
            throw new RuntimeException("Failed to read topology file: File not found or inaccessible", e);
        } catch (Exception e) {
            log.error("Failed to load topology", e);
            throw e;
        }

        // 创建容器
        try {
            log.info("Creating containers for {} nodes", currentTopology.getNodes().size());
            Map<String, String> newContainers = DockerUtil.createContainers(currentTopology.getNodes());

            // 检查是否所有容器都创建成功
            if (newContainers.size() != currentTopology.getNodes().size()) {
                log.error("Failed to create all containers: expected {}, created {}",
                          currentTopology.getNodes().size(), newContainers.size());
                throw new RuntimeException("Failed to create all containers");
            }

            // 检查是否有容器ID为null
            for (Map.Entry<String, String> entry : newContainers.entrySet()) {
                if (entry.getValue() == null || entry.getValue().isEmpty()) {
                    log.error("Container ID is null or empty for container: {}", entry.getKey());
                    throw new RuntimeException("Container ID is null or empty for container: " + entry.getKey());
                }
            }

            containerMap.putAll(newContainers);
            log.info("Successfully created {} containers", newContainers.size());

            // 分配 IP 地址
            Map<String, String> ipAddressMap = new HashMap<>();
            for (Topology.Node node : currentTopology.getNodes()) {
                ipAddressMap.put(node.getName(), node.getIp());
            }

            DockerUtil.startContainers(containerMap.values());
            //todo check
            // 连接容器到网络(顺便启动)现在注释了
            log.info("Connecting containers to network");
            //DockerUtil.connectContainersToNetwork(containerMap, "custom_network", ipAddressMap);
            log.info("Successfully connected containers to network");
        } catch (Exception e) {
            log.error("Failed to create containers or connect to network", e);
            // 清理已创建的容器
            clearNetwork();
            throw e;
        }
    }
    @Override
    public List<TopologyHistory> getTopologyHistory() {
        return topologyHistoryMapper.selectList(null);
    }

    @Override
    public Map<String, Object> loadHistoryTopology(String id) throws Exception {
        TopologyHistory history = topologyHistoryMapper.selectById(id);
        if (history == null) {
            throw new RuntimeException("拓扑文件不存在");
        }

        // 加载拓扑
        loadTopology(history.getFilePath());

        // 返回拓扑数据
        Map<String, Object> result = new HashMap<>();
        result.put("nodes", currentTopology.getNodes());
        result.put("links", currentTopology.getLinks());
        return result;
    }

    @Override
    public void clearNetwork() {
        DockerUtil.removeContainers(containerMap.values().stream().collect(Collectors.toList()));
        containerMap.clear();
        currentTopology = null; // 清空当前拓扑
    }

    @Override
    public int getAttackerCount() {
        if (currentTopology == null) {
            return 0; // 如果没有加载拓扑，返回 0
        }
        return (int) currentTopology.getNodes().stream()
                .filter(node -> "attacker".equalsIgnoreCase(node.getType()))
                .count();
    }

    @Override
    public int getServerCount() {
        if (currentTopology == null) {
            return 0; // 如果没有加载拓扑，返回 0
        }
        return (int) currentTopology.getNodes().stream()
                .filter(node -> "server".equalsIgnoreCase(node.getType()))
                .count();
    }

    @Override
    public void executeAttack(AttackRecordVo attackRecordVo) throws Exception {
        if (currentTopology == null) {
            throw new IllegalStateException("No topology loaded. Please load a topology first.");
        }
        List<String> targets = attackRecordVo.getTargets();
        List<String> attackers = attackRecordVo.getAttackers();
        int attackType = attackRecordVo.getAttackType();

        // 验证目标和攻击者不为空
        if (targets == null || targets.isEmpty()) {
            throw new IllegalArgumentException("Targets cannot be null or empty");
        }
        if (attackers == null || attackers.isEmpty()) {
            throw new IllegalArgumentException("Attackers cannot be null or empty");
        }

        // 获取目标IP地址
        List<String> targetsIp = new ArrayList<>();
        targets.forEach(target -> {
            String targetIp = currentTopology.getNodes().stream()
                    .filter(node -> target.equalsIgnoreCase(node.getName()))
                    .findFirst()
                    .map(Topology.Node::getIp)
                    .orElse(null);

            if (targetIp != null) {
                targetsIp.add(targetIp);
            } else {
                log.warn("Target node not found: {}", target);
            }
        });

        // 如果没有有效的目标IP，抛出异常
        if (targetsIp.isEmpty()) {
            throw new IllegalArgumentException("No valid target IPs found");
        }

        // 执行攻击
        for (String attacker : attackers) {
            String containerId = containerMap.get(attacker);
            if (containerId != null) {
                for (String targetIp : targetsIp) {
                    threadPoolComponent.getExecutorService().submit(() -> {
                        try {
                            // 尝试执行攻击，最多重试3次
                            executeAttackWithRetry(containerId, attackType, targetIp, attacker, 3);
                        } catch (Exception e) {
                            log.error("Failed to execute attack from container: " + attacker, e);
                        }
                    });
                }
            } else {
                throw new IllegalArgumentException("Attacker container not found: " + attacker);
            }
        }
    }

    /**
     * 带重试的攻击执行
     * @param containerId 容器ID
     * @param attackType 攻击类型
     * @param targetIp 目标IP
     * @param attackerName 攻击者名称（用于日志）
     * @param maxRetries 最大重试次数
     * @throws Exception 如果所有重试都失败
     */
    private void executeAttackWithRetry(String containerId, int attackType, String targetIp, String attackerName, int maxRetries) throws Exception {
        int retryCount = 0;
        Exception lastException = null;

        // 首先验证容器是否存在
        try {
            String checkContainerCommand = "docker inspect " + containerId;
            Process process = Runtime.getRuntime().exec(checkContainerCommand);
            int exitCode = process.waitFor();
            if (exitCode != 0) {
                throw new RuntimeException("Container " + containerId + " does not exist");
            }
            log.info("Container {} exists", containerId);
        } catch (Exception e) {
            log.error("Container verification failed: {}", e.getMessage());
            throw new RuntimeException("Container verification failed: " + e.getMessage(), e);
        }

        while (retryCount < maxRetries) {
            try {
                log.info("Executing attack from {} to {}, attempt {}/{}", attackerName, targetIp, retryCount + 1, maxRetries);
                NetworkUtil.executeAttack(containerId, attackType, targetIp);
                log.info("Attack executed successfully from {} to {}", attackerName, targetIp);

//                // 验证攻击是否有效
//                try {
//                    String verificationResult = NetworkUtil.verifyAttack(containerId);
//                    log.info("Attack verification result: {}", verificationResult);
//                } catch (Exception e) {
//                    log.warn("Attack verification failed: {}", e.getMessage());
//                }

                return; // 成功执行，直接返回
            } catch (Exception e) {
                lastException = e;
                log.warn("Attack execution failed, attempt {}/{}: {}", retryCount + 1, maxRetries, e.getMessage());
                retryCount++;

                if (retryCount < maxRetries) {
                    // 等待一段时间再重试
                    log.info("Waiting for {} ms before retry...", 2000);
                    Thread.sleep(2000);
                }
            }
        }

        // 所有重试都失败，抛出最后一个异常
        log.error("All {} attack attempts from {} to {} failed", maxRetries, attackerName, targetIp);
        throw lastException;
    }

    @Override
    public String getServerTrafficInfo(String serverName) throws Exception {
        if (currentTopology == null) {
            throw new IllegalStateException("No topology loaded. Please load a topology first.");
        }

        // 去除可能的引号和空白字符
        if (serverName != null) {
            serverName = serverName.trim();
            if (serverName.startsWith("\"") && serverName.endsWith("\"")) {
                serverName = serverName.substring(1, serverName.length() - 1);
            }
        }

        log.info("Getting traffic info for server: {}", serverName);

        // 根据服务器名称获取对应的容器 ID
        String containerId = containerMap.get(serverName);
        if (containerId == null) {
            log.error("Server not found: {}", serverName);
            throw new IllegalArgumentException("Server not found: " + serverName);
        }

        log.info("Found container ID for server {}: {}", serverName, containerId);

        // 使用工具类发送命令并获取流量信息
        try {
            String trafficInfo = NetworkUtil.getTrafficInfo(containerId);
            log.info("Traffic info for server {}: {}", serverName, trafficInfo);
            return trafficInfo;
        } catch (Exception e) {
            log.error("Failed to get traffic info for server: {}", serverName, e);
            throw new RuntimeException("Failed to get traffic info for server: " + serverName, e);
        }
    }

    @Override
    public String verifyAttack(String attackerName, String targetName) throws Exception {
        if (currentTopology == null) {
            throw new IllegalStateException("No topology loaded. Please load a topology first.");
        }

        // 去除可能的引号和空白字符
        if (attackerName != null) {
            attackerName = attackerName.trim();
            if (attackerName.startsWith("\"") && attackerName.endsWith("\"")) {
                attackerName = attackerName.substring(1, attackerName.length() - 1);
            }
        }

        if (targetName != null) {
            targetName = targetName.trim();
            if (targetName.startsWith("\"") && targetName.endsWith("\"")) {
                targetName = targetName.substring(1, targetName.length() - 1);
            }
        }

        log.info("Verifying attack from attacker: {} to target: {}", attackerName, targetName);

        // 根据攻击者名称获取对应的容器 ID
        String attackerContainerId = containerMap.get(attackerName);
        if (attackerContainerId == null) {
            log.error("Attacker not found: {}", attackerName);
            throw new IllegalArgumentException("Attacker not found: " + attackerName);
        }

        // 根据目标服务器名称获取对应的容器 ID
        String targetContainerId = containerMap.get(targetName);
        if (targetContainerId == null) {
            log.error("Target server not found: {}", targetName);
            throw new IllegalArgumentException("Target server not found: " + targetName);
        }

        log.info("Found container IDs - Attacker: {}, Target: {}", attackerContainerId, targetContainerId);

        // 使用工具类验证攻击
        try {
            String verificationResult = NetworkUtil.verifyAttack(attackerContainerId, targetContainerId);
            log.info("Attack verification completed for attacker: {} to target: {}", attackerName, targetName);
            return verificationResult;
        } catch (Exception e) {
            log.error("Failed to verify attack from attacker: {} to target: {}", attackerName, targetName, e);
            throw new RuntimeException("Failed to verify attack: " + e.getMessage(), e);
        }
    }

    // 计算文件哈希值
    private String calculateFileHash(File file) throws IOException {
        try {
            java.security.MessageDigest digest = java.security.MessageDigest.getInstance("SHA-256");
            byte[] fileBytes = java.nio.file.Files.readAllBytes(file.toPath());
            byte[] hashBytes = digest.digest(fileBytes);
            StringBuilder hexString = new StringBuilder();
            for (byte b : hashBytes) {
                String hex = Integer.toHexString(0xff & b);
                if (hex.length() == 1) {
                    hexString.append('0');
                }
                hexString.append(hex);
            }
            return hexString.toString();
        } catch (Exception e) {
            throw new IOException("Failed to calculate file hash", e);
        }
    }
}
